package com.xbongbong.formula.qlexpress.operator.date;

import com.ql.util.express.Operator;
import com.xbongbong.formula.constant.Constant;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.PaasDateUtil;
import org.apache.commons.lang3.StringUtils;

import java.util.Objects;

/**
 * 返回日期date的年份
 * @author 吴峰
 * @version v1.0
 * @date 2019/8/5 10:47
 * @since v1.0
 */
public class OperatorDays360 extends Operator {

    private static final Integer MONTH = 30;

    private static final Integer REAL_MONTH = 31;

    private static final Integer YEAR= 360;

    public OperatorDays360(String name){
        this.name = name;
    }

    public OperatorDays360(String aliasName, String aName, String errorInfo){
        this.aliasName = aliasName;
        this.name = aName;
        this.errorInfo = errorInfo;
    }


    /**
     * DAYS360按照一年 360 天的算法，返回两个日期间相差的天数
     * 用法：DAYS360(结束日期,开始日期)
     * DAYS360(end_timestamp, start_timestamp, method)
     * 按照一年 360 天的算法（每个月以 30 天计，一年共计 12 个月），DAYS360 函数返回两个日期间相差的天数，这在一些会计计算中将会用到。 如果财会系统是基于一年 12 个月，每月 30 天，可使用此函数帮助计算支付款项。
     * end_timestamp: 必需。结束日期。
     * start_timestamp: 必需。起始日期。
     * method: 可选。用于指定在计算中是采用美国方法还是欧洲方法。不填或false表示是美国方法，true表示是欧洲方法。
     * 方法
     *
     * 定义
     *
     * FALSE 或省略
     *
     * 美国 (NASD) 方法。 如果起始日期是一个月的最后一天，则等于同月的 30 号。 如果终止日期是一个月的最后一天，并且起始日期早于 30 号，则终止日期等于下一个月的 1 号，否则，终止日期等于本月的 30 号。
     *
     * TRUE
     *
     * 欧洲方法。 如果起始日期和终止日期为某月的 31 号，则等于当月的 30 号。
     * @throws Exception
     */
    @Override
    public Object executeInner(Object[] list) throws XbbException {
        if (Objects.isNull(list)) {
            return null;
        }
        boolean method = false;
        if (list.length == Constant.SCALE) {
            method = Boolean.valueOf(String.valueOf(list[2]));
        }
        if (list[0] == null || StringUtils.equals(list[0].toString(), Constant.ZERO_STRING)) {
            return 0;
        }
        if (list[1] == null || StringUtils.equals(list[1].toString(), Constant.ZERO_STRING)) {
            return 0;
        }
        Long startTime = PaasDateUtil.getInt(list[1]);
        Long endTime = PaasDateUtil.getInt(list[0]);

        return method ? europe(endTime, startTime) : american(endTime, startTime);
    }
    /**
     * 美国算法
     * @param endTime 结束时间
     * @param startTime 开始时间
     * @return
     */
    private static int american(Long endTime, Long startTime) {
        Integer startMonth = DateTimeUtil.getMonth4Someday(startTime);
        Integer endMonth = DateTimeUtil.getMonth4Someday(endTime);
        Integer startDay = DateTimeUtil.getDay4Someday(startTime);
        Integer endDay = DateTimeUtil.getDay4Someday(endTime);
        startDay = Objects.equals(startDay, REAL_MONTH) ? MONTH : startDay;
        Integer startYear = DateTimeUtil.getYear4Someday(startTime);
        Integer endYear = DateTimeUtil.getYear4Someday(endTime);
        if (Objects.equals(endDay, REAL_MONTH) && startDay < MONTH) {
            endDay = 1;
            endMonth++;
        }
        return (endYear - startYear) * YEAR + MONTH * (endMonth - startMonth) + endDay - startDay;
    }

    /**
     * 欧洲算法
     * @param endTime 结束时间
     * @param startTime 开始时间
     * @return
     */
    private static int europe(Long endTime, Long startTime) {
        Integer startYear = DateTimeUtil.getYear4Someday(startTime);
        Integer endYear = DateTimeUtil.getYear4Someday(endTime);
        Integer startMonth = DateTimeUtil.getMonth4Someday(startTime);
        Integer endMonth = DateTimeUtil.getMonth4Someday(endTime);
        Integer startDay = DateTimeUtil.getDay4Someday(startTime);
        Integer endDay = DateTimeUtil.getDay4Someday(endTime);
        endDay = Objects.equals(endDay, REAL_MONTH) ? MONTH : endDay;
        startDay = Objects.equals(startDay, REAL_MONTH) ? MONTH : startDay;
        return (endYear - startYear) * YEAR + MONTH * (endMonth - startMonth) + endDay - startDay;
    }

}
